package org.yinxianren.java.test.thread;

import java.util.concurrent.TimeUnit;

/**
 * 当你使用synchronized关键字来保护代码块时，你必须通过一个对象的引用作为参数。通常，
 * 你将会使用this关键字来引用执行该方法的对象，但是你也可以使用其他对象引用。
 * 通常情况下，这些对象被创建只有这个目的。
 * 比如，你在一个类中有被多个线程共享的两个独立属性。你必须同步访问每个变量，
 * 如果有一个线程访问一个属性和另一个线程在同一时刻访问另一个属性，这是没有问题的。
 *
 *
 * 在这个指南中，你将学习如何解决这种情况的一个例子，编程模拟一家电影院有两个屏幕和两个售票处。
 * 当一个售票处出售门票，它们用于两个电影院的其中一个，但不能用于两个，所以在每个电影院的免费席位的数量是独立的属性
 */
public class DemoSynchronized2 {
    public static void main(String[] args) {

        Cinema cinema=new Cinema();

        TicketOffice1 ticketOffice1=new TicketOffice1(cinema);
        Thread thread1=new Thread(ticketOffice1,"TicketOffice1");

        TicketOffice2 ticketOffice2=new TicketOffice2(cinema);
        Thread thread2=new Thread(ticketOffice2,"TicketOffice2");

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Room 1 Vacancies: %d\n",cinema.getVacanciesCinema1());
        System.out.printf("Room 2 Vacancies: %d\n",cinema.getVacanciesCinema2());
    }
}

class Cinema {
    private long vacanciesCinema1;
    private long vacanciesCinema2;

    private final Object controlCinema1;
    private final Object controlCinema2;


    public Cinema(){
        controlCinema1=new Object();
        controlCinema2=new Object();
        vacanciesCinema1=20;
        vacanciesCinema2=20;
    }


    public boolean sellTickets1 (int number) {
        synchronized (controlCinema1) {
            if (number<vacanciesCinema1) {
                vacanciesCinema1-=number;
                return true;
            } else {
                return false;
            }
        }
    }

    public boolean sellTickets2 (int number) {
        synchronized (controlCinema2) {
            if (number<vacanciesCinema2) {
                vacanciesCinema2-=number;
                return true;
            } else {
                return false;
            }
        }
    }


    public boolean returnTickets1 (int number) {
        synchronized (controlCinema1) {
            vacanciesCinema1+=number;
            return true;
        }
    }

    public boolean returnTickets2 (int number) {
        synchronized (controlCinema2) {
            vacanciesCinema2+=number;
            return true;
        }
    }

    public long getVacanciesCinema1() {
        return vacanciesCinema1;
    }

    public long getVacanciesCinema2() {
        return vacanciesCinema2;
    }

}

class TicketOffice1 implements Runnable {

    private Cinema cinema;
    public TicketOffice1 (Cinema cinema) {
        this.cinema=cinema;
    }

    @Override
    public void run() {
        cinema.sellTickets1(3);
        cinema.sellTickets1(2);
        cinema.sellTickets2(2);
        cinema.returnTickets1(3);
        cinema.sellTickets1(5);
        cinema.sellTickets2(2);
        cinema.sellTickets2(2);
        cinema.sellTickets2(2);
    }
}


class TicketOffice2 implements Runnable {

    private Cinema cinema;
    public TicketOffice2 (Cinema cinema) {
        this.cinema=cinema;
    }

    @Override
    public void run() {
        cinema.sellTickets2(2);
        cinema.sellTickets2(4);
        cinema.sellTickets1(2);
        cinema.sellTickets1(1);
        cinema.returnTickets2(2);
        cinema.sellTickets1(3);
        cinema.sellTickets2(2);
        cinema.sellTickets1(2);
    }
}
